﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using Devart.Data.Oracle;
using System.Data;
using DbSharp.Utils.Db;
using System.Globalization;
using DbSharp.Framework.DbObject;
using DbSharp.Framework.Common;
using DbSharp.Framework.Driver.FieldPersistence;


namespace DbSharp.Framework.Driver
{
    class OracleTestPlugin2 : ITestDataSource
    {
        #region Private method

        /// <summary>
        /// remove all semicolon symbols tailed the sql statement
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        private string RemoveTailedSemiColons(string sql)
        {
            return SqlTextHelper.RightTrimCharacter(sql, ';');
        }


        private int GetArgumentType(string argumentTypeText)
        {
            OracleDbType parameterType;
            string upperTypeStr = argumentTypeText.Trim().ToUpper();
            switch (upperTypeStr)
            {
                case "ORA_CHAR":
                    parameterType = OracleDbType.Char;
                    break;
                case "ORA_VARCHAR":
                case "ORA_VARCHAR2":
                    parameterType = OracleDbType.VarChar;
                    break;
                case "ORA_NCHAR":
                    parameterType = OracleDbType.NChar;
                    break;
                case "ORA_NVARCHAR":
                case "ORA_NVARCHAR2":
                    parameterType = OracleDbType.NVarChar;
                    break;
                case "ORA_NUMBER":
                    parameterType = OracleDbType.Number;
                    break;
                case "ORA_DATETIME":
                    parameterType = OracleDbType.Date;
                    break;
                case "ORA_TIMESTAMP":
                    parameterType = OracleDbType.TimeStamp;
                    break;
                case "ORA_TIMESTAMPLOCAL":
                    parameterType = OracleDbType.TimeStampLTZ;
                    break;
                case "ORA_TIMESTAMPWITHTZ":
                    parameterType = OracleDbType.TimeStampTZ;
                    break;
                case "ORA_INTERVALDAYTOSECOND":
                    parameterType = OracleDbType.IntervalDS;
                    break;
                case "ORA_INTERVALYEARTOMONTH":
                    parameterType = OracleDbType.IntervalYM;
                    break;
                case "ORA_INT":
                    parameterType = OracleDbType.Integer;
                    break;
                case "ORA_FLOAT":
                case "ORA_REAL":
                    parameterType = OracleDbType.Float;
                    break;
                case "ORA_DOUBLE":
                    parameterType = OracleDbType.Double;
                    break;
                case "ORA_ROWID":
                    parameterType = OracleDbType.RowId;
                    break;
                case "ORA_CURSOR":
                    parameterType = OracleDbType.Cursor;
                    break;
                default:
                    parameterType = OracleDbType.VarChar;
                    break;
            }
            return (int)parameterType;
        }

        #endregion



        #region ITestDataSource Members

        public DateTime GetDbServerTime(DbConnection conn)
        {
            string sql = "Select sysdate from dual ";
            DbCommand cmd = new OracleCommand();
            cmd.Connection = conn;
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sql;
            DateTime result = (DateTime)cmd.ExecuteScalar();
            return result;
        }

        public bool IsRecordsetTypeArgument(string argumentTypeText)
        {
            if (GetArgumentType(argumentTypeText) == (int)OracleDbType.Cursor)
                return true;
            else
                return false;
        }

        public bool IsRecordsetTypeParameter(DbParameter param)
        {
            if (param == null)
                return false;
            else if (((OracleParameter)param).OracleDbType == OracleDbType.Cursor)
                return true;
            else
                return false;
        }

        public DbConnection CreateConnection(string connString)
        {
            var cnn = new OracleConnection();
            cnn.ConnectionString = connString;
            return cnn;
        }

        public DbCommand PrepareCommand(IStatementDeclaration statement)
        {
            DbCommand cmd = new OracleCommand();
            cmd.CommandType = statement.CommandType;

            IList<StatementArgument> listArgumentObj = statement.ListArgument;
            foreach (var argumentObj in listArgumentObj)
            {
                if (argumentObj.Direction == ArgumentDirection.ClientSide)
                {
                    //Clientside parameter will not added into ParameterList for ever
                    continue;
                }
                else if (argumentObj.IsSqlStmtResultArg)
                {
                    //It means this argument is virtual argument, not the real argument
                    // So the argument will not be added into ParameterList 
                    continue;
                }
                else
                {
                    //Oracle always use named arguments to hold resultsets, so we need add these return type arguments into parameterlist
                }

                OracleParameter parameter = new OracleParameter();
                parameter.Direction = ArgumentDirectionHelper.ConvertToParamDirection(argumentObj.Direction);
                parameter.OracleDbType = (OracleDbType)GetArgumentType(argumentObj.TypeStr);
                parameter.Size = argumentObj.Size;
                parameter.ParameterName = NormalizeArgumentName(argumentObj.Name);

                cmd.Parameters.Add(parameter);
            }

            return cmd;
        }

        public void UpdateCommandText(DbCommand cmd, string newCmdText)
        {
            cmd.CommandText = newCmdText.Trim();
            //make sure the sql statement does not end with semicolon
            if (cmd.CommandType == CommandType.Text)
            {
                cmd.CommandText = RemoveTailedSemiColons(cmd.CommandText);
            }
        }

        public void AssignParameterValue(DbParameter param, string value, string dateTimeFormat)
        {
            OracleParameter parameter = (OracleParameter)param;

            if ((parameter.OracleDbType == OracleDbType.Date) ||
                (parameter.OracleDbType == OracleDbType.TimeStamp) ||
                (parameter.OracleDbType == OracleDbType.TimeStampLTZ) ||
                (parameter.OracleDbType == OracleDbType.TimeStampTZ))
            {
                if (string.IsNullOrEmpty(dateTimeFormat))
                    parameter.Value = DateTimeParser.ParseUtc(value);
                else
                {
                    parameter.Value = DateTimeParser.ParseExactUtc(value, dateTimeFormat);
                }
            }
            else if (parameter.OracleDbType == OracleDbType.IntervalDS)
                parameter.Value = OracleIntervalDS.Parse(value);
            else if (parameter.OracleDbType == OracleDbType.IntervalYM)
                parameter.Value = OracleIntervalYM.Parse(value);
            else
                parameter.Value = value;
        }

        public bool TransactionSupported
        {
            get { return true; }
        }

        public bool HoldResultsetMustWithNamedArgInSP
        {
            get { return true; }
        }

        public string NormalizeArgumentName(string argumentName)
        {
            return SqlTextHelper.LeftTrimCharacter(argumentName, ':');
        }

       


        private IFieldPersistence m_FieldPersistenceService = new OracleFieldPersistence2();
        public IFieldPersistence FieldPersistenceService
        {
            get { return m_FieldPersistenceService; }
        }



        #endregion
    }
}
